home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / os2 / cenv2_19.arj / UNHANG.CMM < prev    next >
Text File  |  1994-03-08  |  13KB  |  383 lines

  1. //****************************************************************
  2. //*** UnHang.cmm - CEnvi program to monitor PM queues and kill ***
  3. //*** ver.1        or otherwise handle session that have hung  ***
  4. //***              queues to allow the system to continue.     ***
  5. //****************************************************************
  6.  
  7. #include <OptParms.lib>
  8.  
  9. main(argc,argv)
  10. {
  11.    ParseInputArgs(argc,argv);
  12.    if ( 1 != argc ) {
  13.       // All the args weren't parsed, and so invalid input.
  14.       // show how to use this program.
  15.       Instructions();
  16.    } else {
  17.  
  18.       if ( defined(LineCount) )
  19.          system("mode 80,%d",LineCount);
  20.       if ( HighPriority )
  21.          RunAtHighPriority();
  22.       if ( Hide )
  23.          HideMyself();
  24.       UnhangForever();
  25.    }
  26. }
  27.  
  28. #define DEFAULT_MAX_HANGTIME       60     // how many seconds, max (approximately)
  29.                                   // can a program hang before we kill it
  30. #define DEFAULT_CYCLES_PER_MAX_HANGTIME 3 // break test down to this many times
  31.                                   // maximum hang time, more cycles gives
  32.                                   // each program a better chance to not
  33.                                   // be hung, but takes up more time
  34. #define DEFAULT_MINIMUM_SUSPEND_FACTOR   0  // don't suspend less than this long
  35.  
  36.  
  37. Instructions()
  38. {
  39.    printf("\a\n");
  40.    printf("UnHang.cmm - Prevent the workplace shell from hanging due to a bad app.\n");
  41.    printf("\n");
  42.    printf("SYNTAX: CEnvi UnHang.cmm [Options]\n");
  43.    printf("\n");
  44.    printf("Options: /LINES=linecount - how many lines to display in the output\n");
  45.    printf("         /ONTOP - keep this screen on top of other windows\n");
  46.    printf("         /HIDE - Hide this window\n");
  47.    printf("         /LOG=FileSpec - Filename to log hangs and attempts to unhang\n");
  48.    printf("         /HIGH - Run at high priority\n");
  49.    printf("         /TIMEOUT=# - how many seconds (max) for complete test; default %d\n", DEFAULT_MAX_HANGTIME);
  50.    printf("         /CYCLES=# - how many mini-test cycles per timeout; default %d\n", DEFAULT_CYCLES_PER_MAX_HANGTIME);
  51.    printf("         /SUSPEND=# - minimum time to suspend between checks; this keeps from\n");
  52.    printf("                      \"hogging\" the cpu; 0 is bad if /HIGH; default %d\n",DEFAULT_MINIMUM_SUSPEND_FACTOR);
  53.    printf("         /COMMAND=cmd - will call this command if an application seems hung,\n");
  54.    printf("                        sending Process ID as first parameter and process name\n");
  55.    printf("                        as second parameter. Default is to kill the app.\n");
  56.    printf("\n");
  57. }
  58.  
  59. LineCount;
  60. OnTop;
  61. Hide;
  62. LogFileName;
  63. HighPriority;
  64. MaxHangtime = DEFAULT_MAX_HANGTIME;
  65. CyclesPerMaxHangtime = DEFAULT_CYCLES_PER_MAX_HANGTIME;
  66. MinimumSuspendFactor = DEFAULT_MINIMUM_SUSPEND_FACTOR;
  67. HangCommand;
  68.  
  69. ParseInputArgs(argc,argv)
  70. {
  71.    if ( OptionalParameter(argc,argv,"LINES",lTemp)
  72.      && (LineCount=atoi(lTemp)) < 1 )
  73.       printf("Invalid LINES parameter\a\n"), abort();
  74.    OnTop = OptionalParameter(argc,argv,"ONTOP");
  75.    Hide = OptionalParameter(argc,argv,"HIDE");
  76.    OptionalParameter(argc,argv,"LOG",LogFileName);
  77.    HighPriority = OptionalParameter(argc,argv,"HIGH");
  78.    if ( OptionalParameter(argc,argv,"TIMEOUT",lTemp)
  79.      && (MaxHangtime=atoi(lTemp)) < 1 )
  80.       printf("Invalid TIMEOUT parameter\a\n"), abort();
  81.    if ( OptionalParameter(argc,argv,"CYCLES",lTemp)
  82.      && (CyclesPerMaxHangtime=atoi(lTemp)) < 1 )
  83.       printf("Invalid CYCLES parameter\a\n"), abort();
  84.    if ( OptionalParameter(argc,argv,"SUSPEND",lTemp)
  85.      && (MinimumSuspendFactor=atoi(lTemp)) < 0 )
  86.       printf("Invalid SUSPEND parameter\a\n"), abort();
  87.    OptionalParameter(argc,argv,"COMMAND",HangCommand);
  88. }
  89.  
  90.  
  91. /////////////////////////////////////////////////////////////////////////
  92.  
  93. #define HWND_DESKTOP 1
  94. #define HWND_OBJECT  2
  95.  
  96. SuspendFactor;
  97.  
  98. UnhangForever()
  99. {
  100.    // initialize suspend factor - 25 sounds like a good number to start with
  101.    SuspendFactor = max(25,MinimumSuspendFactor);
  102.  
  103.    // initialize timers to know loops are run frequently enough
  104.    TimeBetweenLoops = MaxHangtime / CyclesPerMaxHangtime ;
  105.    PMShellProblemTime = 0; // PMSHELL gets treated special; we'll let it have
  106.                            // a little extra hung time to allow some other
  107.                            // process to hang first
  108.    PMShellTimeout = MaxHangtime * (CyclesPerMaxHangtime + 2) / CyclesPerMaxHangtime;
  109.  
  110.    for( ; ; ) { // FOREVER
  111.  
  112.       LoopStartTime = time();
  113.  
  114.       if ( OnTop )
  115.          PutMyselfOnTop();
  116.  
  117.       // build an array of all the queue from the desktop on down
  118.       QueueList = BuildQueueList();
  119.       PList = ProcessList();
  120.  
  121.       // Since it does no good to kill the very first queue, then don't test it
  122.       //QueueList++;
  123.  
  124.       // For each queue, see if it is hung
  125.       printf("\n\nProcess      ID    Queue   Size   Status\n");
  126.       printf("---------- ------  -----  ------  ------");
  127.       PMShellProblem = False;
  128.       for ( q = GetArraySpan(QueueList); 0 <= q; q-- ) {
  129.  
  130.          hmq = QueueList[q];
  131.          if SuspendFactor suspend(SuspendFactor);
  132.  
  133.          if ( GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize) ) {
  134.             printf("\n%-10.10s   %-5d %04X    %-6d  ",ProcessName,ProcessID,hmq,QueueSize);
  135.             if ( HungQueue(hmq) ) {
  136.                printf("HUNG");
  137.  
  138.                // If this is PMSHELL, then give it a little extra time
  139.                if ( !strcmpi("PMSHELL",ProcessName) ) {
  140.                   PMShellProblem = True;
  141.                   if ( 0 == PMShellProblemTime )
  142.                      PMShellProblemTime = time();
  143.                   if ( difftime(time(),PMShellProblemTime) < PMShellTimeout )
  144.                      continue;
  145.                }
  146.                LogFile("Process %s, id = %d, queue = %04X, size = %d, hung\n",
  147.                        ProcessName,ProcessID,hmq,QueueSize);
  148.                AttendToHungProcess(ProcessID,ProcessName);
  149.                printf(" AND KILLED!\a\a\a");
  150.             } else {
  151.                printf("OK");
  152.             }
  153.          }
  154.  
  155.       }
  156.       if ( !PMShellProblem )
  157.          PMShellProblemTime = 0;
  158.  
  159.       // Adjust SuspendFactor to try to even-out processor time used by UNHANG.
  160.       // If extra time is available then increase SuspendFactor.  If not
  161.       // enough time is available then decrease it.
  162.       ElapsedTestTime = difftime(time(),LoopStartTime);
  163.       ExcessTime = TimeBetweenLoops - ElapsedTestTime;
  164.       SuspendFactor = max(MinimumSuspendFactor,SuspendFactor + ExcessTime/CyclesPerMaxHangtime);
  165.       if ( 0 < ExcessTime ) {
  166.          // a little time left over, so wait until time is up
  167.          while ( difftime(time(),LoopStartTime) < TimeBetweenLoops )
  168.             suspend(SuspendFactor);
  169.       }
  170.       printf("\n   New SuspendFactor = %d (from excess %d)\n",SuspendFactor,ExcessTime);
  171.    }
  172. }
  173.  
  174.  
  175. LogFile(Format)
  176. {
  177.    if ( defined(LogFileName) ) {
  178.       if ( fp = fopen(LogFileName,"a") ) {
  179.          fprintf(fp,"\n");
  180.          fprintf(fp,ctime(time()));
  181.          va_start(valist,Format);
  182.          vfprintf(fp,Format,valist);
  183.          va_end(valist);
  184.          fclose(fp);
  185.       }
  186.    }
  187. }
  188.  
  189. BuildQueueList() // build array of all desktop window handles
  190. {
  191.    AddQueueForWindowAndChildren(QList,HWND_DESKTOP);
  192.    //AddQueueForWindowAndChildren(QList,HWND_OBJECT);
  193.    return(QList);
  194. }
  195.  
  196. AddQueueForWindowAndChildren(QList,ParentHandle)
  197. {
  198.    // Get the queue for this parent handle
  199.    Queue = GetWindowQueue(ParentHandle);
  200.  
  201.    // If this queue is not already in the queue list, then add it
  202.    if ( !defined(QList) ) {
  203.       QList[0] = Queue;
  204.    } else {
  205.       for ( i = GetArraySpan(QList); 0 <= i; i-- ) {
  206.          if ( Queue == QList[i] )
  207.             break;
  208.       }
  209.       if ( i < 0 )
  210.          QList[1+GetArraySpan(QList)] = Queue;
  211.    }
  212.  
  213.    // recursively call this routine for all its children
  214.    EnumHandle = WinBeginEnumWindows(ParentHandle);
  215.    while ( NULL != (WinHandle = WinGetNextWindow(EnumHandle)) ) {
  216.       if SuspendFactor suspend(SuspendFactor);
  217.       AddQueueForWindowAndChildren(QList,WinHandle);
  218.    }
  219.    WinEndEnumWindows(EnumHandle);
  220.    if SuspendFactor suspend(SuspendFactor);
  221. }
  222.  
  223.  
  224. HungQueue(hmq) // return True if this queue is hung
  225. {
  226.    if ( !GetQueueInfo(hmq,MsgCount,ProcessID) )
  227.       // this no longer appears to be a queue; and so never mind
  228.       return(False);
  229.  
  230.    // determine if queue is hung by sending lots of NULL message to it
  231.    // then wait a second and if sending null message fails then it is hung
  232.    MsgCountPerLoop = MsgCount / CyclesPerMaxHangtime ;
  233.    for ( i = MsgCountPerLoop; 0 < i--; ) {
  234.       if SuspendFactor suspend(SuspendFactor);
  235.       if ( !PostNullMsgToQueue(hmq) ) {
  236.          // there appears to have been an error posting a null message.
  237.          // and so the queue must be full (or no longer valid).  Wait
  238.          // some seconds and give it one more chance
  239.          suspend( 5 * 1000 );
  240.          return( !PostNullMsgToQueue(hmq)
  241.               && GetQueueInfo(hmq,MsgCount,ProcessID) );
  242.       }
  243.    }
  244.    return(False);
  245. }
  246.  
  247. WinBeginEnumWindows(pHwndParent)
  248. {
  249.    #define ORD_WIN32BEGINENUMWINDOWS   702
  250.    return DynamicLink("PMWIN",ORD_WIN32BEGINENUMWINDOWS,BIT32,CDECL,pHwndParent)
  251. }
  252.  
  253. WinGetNextWindow(pHEnum)
  254. {
  255.    #define ORD_WIN32GETNEXTWINDOW      756
  256.    return DynamicLink("PMWIN",ORD_WIN32GETNEXTWINDOW,BIT32,CDECL,pHEnum)
  257. }
  258.  
  259. WinEndEnumWindows(pHEnum)
  260. {
  261.    #define ORD_WIN32ENDENUMWINDOWS     737
  262.    return DynamicLink("PMWIN",ORD_WIN32ENDENUMWINDOWS,BIT32,CDECL,pHEnum)
  263. }
  264.  
  265. GetWindowQueue(hwnd)
  266. {
  267.    #define ORD_WIN32QUERYWINDOWULONG   843
  268.    #define QWL_HMQ   (-4)
  269.    return DynamicLink("PMWIN",ORD_WIN32QUERYWINDOWULONG,BIT32,CDECL,
  270.                       hwnd,QWL_HMQ)
  271. }
  272.  
  273. GetQueueInfo(hmq,count,pid)
  274. {
  275.    #define ORD_WIN32QUERYQUEUEINFO  824
  276.    BLOBSize(qinfo,4 * 5);
  277.    success = DynamicLink("PMWIN",ORD_WIN32QUERYQUEUEINFO,BIT32,CDECL,
  278.                          hmq,qinfo,BLObSize(qinfo));
  279.    if ( success ) {
  280.       pid = BLObGet(qinfo,4 * 1,UWORD32);
  281.       count = BLObGet(qinfo,4 * 3,UWORD32);
  282.    }
  283.    return(success);
  284. }
  285.  
  286. GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize)
  287.    // return data about this queue and who owns it; False if cannot
  288. {
  289.    if ( !GetQueueInfo(hmq,QueueSize,ProcessID) )
  290.       return(False);
  291.  
  292.    // to find process name, match processID to id in PList
  293.    for ( p = GetArraySpan(PList); 0 <= p; p-- ) {
  294.       if ( ProcessID == PList[p].id ) {
  295.          ProcessName = SplitFileName(PList[p].name).name;
  296.          return(True);
  297.       }
  298.    }
  299.    // if got here, then all OK except for process name
  300.    ProcessName = "?????";
  301.    return(True);
  302. }
  303.  
  304. PostNullMsgToQueue(hmq)
  305. {
  306.    #define ORD_WIN32POSTQUEUEMSG 902
  307.    #define WM_NULL   0
  308.    return DynamicLink("PMWIN",ORD_WIN32POSTQUEUEMSG,BIT32,CDECL,
  309.                       hmq,WM_NULL,MessageID,0,0)
  310. }
  311.  
  312. DeadList;   // keep a list of processes already killed
  313. AttendToHungProcess(id,name)
  314. {
  315.    // determine if this process already (attempted) killed
  316.    if ( !defined(DeadList) ) {
  317.       DeadList[0] = id;
  318.    } else {
  319.       for ( _d = GetArraySpan(DeadList); 0 <= _d; _d-- ) {
  320.          if ( DeadList[_d] == id ) {
  321.             AttendToHungProcessParent(id);
  322.          }
  323.       }
  324.       if ( _d < 0 ) {
  325.          // this id hasn't been killed before; add to list
  326.          DeadList[GetArraySpan(DeadList)+1] = id;
  327.       }
  328.    }
  329.  
  330.    if ( !defined(HangCommand) ) {
  331.       #define DKP_PROCESSTREE    0  // kill process and all descendents, if created by this process
  332.       #define DKP_PROCESS        1  // kill any process even if not created by this process
  333.       #define ORD_DOS32KILLPROCESS  235
  334.       DynamicLink("doscalls",ORD_DOS32KILLPROCESS,BIT32,CDECL,DKP_PROCESS,id);
  335.    } else {
  336.       system("%s %d %s",HangCommand,id,name);
  337.    }
  338. }
  339.  
  340. AttendToHungProcessParent(id)
  341. {
  342.    // get parent of this id, and then KILL IT
  343.    list = ProcessList();
  344.    // find entry for this process ID
  345.    for ( proc = GetArraySpan(list); 0 <= proc; proc-- ) {
  346.       if ( list[proc].id == id ) {
  347.          parent = list[proc].parent;
  348.          for ( _i = GetArraySpan(list); 0 <= _i; _i-- ) {
  349.             if ( list[_i].id == parent ) {
  350.                LogFile("Kill Parent Process %s, id = %d\n",
  351.                        list[_i].name,parent);
  352.                AttendToHungProcess(parent,list[_i].name);
  353.             }
  354.          }
  355.       }
  356.    }
  357. }
  358.  
  359. RunAtHighPriority() // make this a high priority process
  360. {
  361.    #define ORD_DOS32SETPRIORITY  236
  362.    #define PRTYC_TIMECRITICAL 3
  363.    DynamicLink("doscalls",ORD_DOS32SETPRIORITY,BIT32,CDECL,0,PRTYC_TIMECRITICAL,0,0)
  364. }
  365.  
  366. HideMyself()
  367. {
  368.    #define SWP_HIDE              0x0010
  369.    #define ORD_WIN32SETWINDOWPOS 875
  370.    DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
  371.                Info().WinHandle,0,0,0,0,0,SWP_HIDE);
  372. }
  373.  
  374. PutMyselfOnTop()
  375. {
  376.    #define HWND_TOP     3
  377.    #define SWP_ZORDER   4
  378.    #define ORD_WIN32SETWINDOWPOS 875
  379.    DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
  380.                Info().WinHandle,HWND_TOP,0,0,0,0,SWP_ZORDER);
  381. }
  382.  
  383.